🆕 Go SDK

Help Center

With DocuGenerate, you can create PDF and Word documents directly from your Go application. This guide shows how to call each API method from Go 1.18 or later. For the full list of parameters and responses, see the API Reference.

Summary

1. Authentication
2. Create template
3. List templates
4. Get template
5. Update template
6. Delete template
7. Generate document
8. List documents
9. Get document
10. Update document
11. Delete document

1. Authentication

Every request is authenticated by sending your API Key in the Authorization header. Store the key in an environment variable rather than hardcoding it in your source code:

export DOCUGENERATE_API_KEY="YOUR-API-KEY"

The examples use the net/http, mime/multipart and encoding/json packages from the Go standard library, so there’s nothing to install. Each example is written as the body of the main function of a program with these imports and declarations. Remove the imports an example doesn’t use, since Go doesn’t compile unused imports. Since net/http doesn’t return an error on HTTP error statuses, each example checks the status code before reading the response:

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"mime/multipart"
	"net/http"
	"os"
)

const apiURL = "https://api.docugenerate.com/v1"

var apiKey = os.Getenv("DOCUGENERATE_API_KEY")

func main() {
	// Add the example code here
}

If your account stores data in another region, replace the base URL with the matching regional endpoint, for example https://api.eu.docugenerate.com/v1.

2. Create template

To create a template, upload the template file with a POST /template request. This endpoint requires the multipart/form-data content type, built here with a multipart.Writer:

file, err := os.Open("Business Letter.docx")
if err != nil {
	panic(err)
}
defer file.Close()

var form bytes.Buffer
writer := multipart.NewWriter(&form)
part, err := writer.CreateFormFile("file", "Business Letter.docx")
if err != nil {
	panic(err)
}
if _, err := io.Copy(part, file); err != nil {
	panic(err)
}
writer.WriteField("name", "Business Letter")
writer.Close()

request, err := http.NewRequest("POST", apiURL+"/template", &form)
if err != nil {
	panic(err)
}
request.Header.Set("Authorization", apiKey)
request.Header.Set("Accept", "application/json")
request.Header.Set("Content-Type", writer.FormDataContentType())

response, err := http.DefaultClient.Do(request)
if err != nil {
	panic(err)
}
defer response.Body.Close()

body, err := io.ReadAll(response.Body)
if err != nil {
	panic(err)
}

if response.StatusCode >= 400 {
	panic(fmt.Sprintf("DocuGenerate API error %d: %s", response.StatusCode, body))
}

var template map[string]any
if err := json.Unmarshal(body, &template); err != nil {
	panic(err)
}
fmt.Println(template["id"])

Always set the Content-Type header with writer.FormDataContentType(), which includes the multipart boundary. Without the boundary, the request fails. The response contains the new template, including the tags detected automatically in the file:

{
  "enhanced_syntax": false,
  "versioning_enabled": false,
  "folder": [],
  "tags": {
    "valid": [
      "Date",
      "Name",
      "Job Title",
      "Company Name",
      "Street Address",
      "City",
      "State",
      "Zip Code",
      "Email",
      "Phone"
    ],
    "invalid": []
  },
  "created": 1791055374301,
  "updated": 1791055374301,
  "name": "Business Letter",
  "delimiters": {
    "left": "[",
    "right": "]"
  },
  "filename": "Business Letter.docx",
  "format": ".docx",
  "region": "eu",
  "page_count": 1,
  "image_uri": "https://firebasestorage.googleapis.com/v0/b/storage.eu.docugenerate.com/o/templates%2FuVE30i1427KQsYcED0bl%2FBusiness%20Letter.png?alt=media&token=0ce64a4b-495a-426b-8783-42b479f7ae38",
  "preview_uri": "https://firebasestorage.googleapis.com/v0/b/storage.eu.docugenerate.com/o/templates%2FuVE30i1427KQsYcED0bl%2FBusiness%20Letter.pdf?alt=media&token=0b3939c7-824e-4979-9aca-ca4a87175cbf",
  "template_uri": "https://firebasestorage.googleapis.com/v0/b/storage.eu.docugenerate.com/o/templates%2FuVE30i1427KQsYcED0bl%2FBusiness%20Letter.docx?alt=media&token=d37a4458-3620-48db-94b8-6ac46f0442ab",
  "id": "uVE30i1427KQsYcED0bl"
}

Keep the template id, as you’ll need it to generate documents. The following optional parameters can also be written to the form:

  • delimiters: The delimiters used to detect the tags, sent as a JSON string, e.g. {"left": "[", "right": "]"}. By default, they’re determined automatically.
  • region: Where the template and its generated documents are stored, either us, eu, uk or au. By default, the account’s default region is used.
  • enhanced_syntax: Set to "true" to use nested properties and logical or mathematical operators in the tags.
  • versioning_enabled: Set to "true" to keep previous versions of the file when uploading a new one, if your plan allows it.
  • folder: The folder of the template, from root to leaf. Missing folders are created automatically.

To place the template in a folder, write one folder field for each level of the path. For example, to place it in the Letters > Business folder:

writer.WriteField("folder", "Letters")
writer.WriteField("folder", "Business")

3. List templates

A GET /template request returns all the templates in your account:

request, err := http.NewRequest("GET", apiURL+"/template", nil)
if err != nil {
	panic(err)
}
request.Header.Set("Authorization", apiKey)
request.Header.Set("Accept", "application/json")

response, err := http.DefaultClient.Do(request)
if err != nil {
	panic(err)
}
defer response.Body.Close()

body, err := io.ReadAll(response.Body)
if err != nil {
	panic(err)
}

if response.StatusCode >= 400 {
	panic(fmt.Sprintf("DocuGenerate API error %d: %s", response.StatusCode, body))
}

var templates []map[string]any
if err := json.Unmarshal(body, &templates); err != nil {
	panic(err)
}
for _, template := range templates {
	fmt.Println(template["id"], template["name"])
}

To only list the templates of a folder, repeat the folder query parameter for each level of the path. For example, to list the templates in the Letters > Business folder:

request, err := http.NewRequest("GET", apiURL+"/template?folder=Letters&folder=Business", nil)
if err != nil {
	panic(err)
}
request.Header.Set("Authorization", apiKey)
request.Header.Set("Accept", "application/json")

response, err := http.DefaultClient.Do(request)
if err != nil {
	panic(err)
}
defer response.Body.Close()

body, err := io.ReadAll(response.Body)
if err != nil {
	panic(err)
}

if response.StatusCode >= 400 {
	panic(fmt.Sprintf("DocuGenerate API error %d: %s", response.StatusCode, body))
}

Only the templates placed directly in that folder are returned. Templates in its subfolders aren’t included. To learn more, see how to organize templates in folders with the API.

4. Get template

To retrieve a single template, call GET /template/{id} with its ID:

templateID := "bet2oQirk0pSd9ctH9Qu"

request, err := http.NewRequest("GET", apiURL+"/template/"+templateID, nil)
if err != nil {
	panic(err)
}
request.Header.Set("Authorization", apiKey)
request.Header.Set("Accept", "application/json")

response, err := http.DefaultClient.Do(request)
if err != nil {
	panic(err)
}
defer response.Body.Close()

body, err := io.ReadAll(response.Body)
if err != nil {
	panic(err)
}

if response.StatusCode >= 400 {
	panic(fmt.Sprintf("DocuGenerate API error %d: %s", response.StatusCode, body))
}

var template map[string]any
if err := json.Unmarshal(body, &template); err != nil {
	panic(err)
}
fmt.Println(template["tags"].(map[string]any)["valid"])

This is useful, for example, to check which merge tags the template expects before generating documents.

5. Update template

A PUT /template/{id} request updates a template. All the parameters are optional, so only send the ones you want to change. For example, to upload a new version of the file and rename the template:

templateID := "bet2oQirk0pSd9ctH9Qu"

file, err := os.Open("Business Letter v2.docx")
if err != nil {
	panic(err)
}
defer file.Close()

var form bytes.Buffer
writer := multipart.NewWriter(&form)
part, err := writer.CreateFormFile("file", "Business Letter v2.docx")
if err != nil {
	panic(err)
}
if _, err := io.Copy(part, file); err != nil {
	panic(err)
}
writer.WriteField("name", "Business Letter v2")
writer.Close()

request, err := http.NewRequest("PUT", apiURL+"/template/"+templateID, &form)
if err != nil {
	panic(err)
}
request.Header.Set("Authorization", apiKey)
request.Header.Set("Accept", "application/json")
request.Header.Set("Content-Type", writer.FormDataContentType())

response, err := http.DefaultClient.Do(request)
if err != nil {
	panic(err)
}
defer response.Body.Close()

body, err := io.ReadAll(response.Body)
if err != nil {
	panic(err)
}

if response.StatusCode >= 400 {
	panic(fmt.Sprintf("DocuGenerate API error %d: %s", response.StatusCode, body))
}

var template map[string]any
if err := json.Unmarshal(body, &template); err != nil {
	panic(err)
}

As when creating a template, the body must be multipart/form-data, with the Content-Type header set to writer.FormDataContentType(). Besides file and name, the following optional parameters can be written to the form:

  • delimiters: The new delimiters, sent as a JSON string. If provided, the template is parsed again to detect the merge tags based on the new delimiters. When a new file is uploaded without delimiters, the current delimiters are used.
  • region: Moves the template to another region, either us, eu, uk or au. Documents generated afterwards are stored in the new region, while existing documents stay in their current region.
  • folder: Moves the template to another folder, with one folder field for each level of the path. Send "[]" to move the template out of any folder.
  • enhanced_syntax: Set to "true" or "false" to enable or disable the enhanced syntax.
  • versioning_enabled: Set to "true" or "false" to enable or disable the version history, if your plan allows it.

6. Delete template

To delete a template, send a DELETE /template/{id} request. The API responds with a 204 No Content status on success:

templateID := "bet2oQirk0pSd9ctH9Qu"

request, err := http.NewRequest("DELETE", apiURL+"/template/"+templateID, nil)
if err != nil {
	panic(err)
}
request.Header.Set("Authorization", apiKey)
request.Header.Set("Accept", "application/json")

response, err := http.DefaultClient.Do(request)
if err != nil {
	panic(err)
}
defer response.Body.Close()

body, err := io.ReadAll(response.Body)
if err != nil {
	panic(err)
}

if response.StatusCode >= 400 {
	panic(fmt.Sprintf("DocuGenerate API error %d: %s", response.StatusCode, body))
}

7. Generate document

You generate documents with a POST /document request, passing the template_id and the data used to replace the merge tags:

payload, err := json.Marshal(map[string]any{
	"template_id": "bet2oQirk0pSd9ctH9Qu",
	"data": map[string]string{
		"Date":           "October 4, 2026",
		"Name":           "Emily Carter",
		"Job Title":      "Operations Manager",
		"Company Name":   "Harbor Point Consulting",
		"Street Address": "118 West Street",
		"City":           "Annapolis",
		"State":          "Maryland",
		"Zip Code":       "21405",
		"Email":          "emily.carter@example.com",
		"Phone":          "(410) 555-0142",
	},
	"output_format": ".pdf",
})
if err != nil {
	panic(err)
}

request, err := http.NewRequest("POST", apiURL+"/document", bytes.NewReader(payload))
if err != nil {
	panic(err)
}
request.Header.Set("Authorization", apiKey)
request.Header.Set("Accept", "application/json")
request.Header.Set("Content-Type", "application/json")

response, err := http.DefaultClient.Do(request)
if err != nil {
	panic(err)
}
defer response.Body.Close()

body, err := io.ReadAll(response.Body)
if err != nil {
	panic(err)
}

if response.StatusCode >= 400 {
	panic(fmt.Sprintf("DocuGenerate API error %d: %s", response.StatusCode, body))
}

var document map[string]any
if err := json.Unmarshal(body, &document); err != nil {
	panic(err)
}
fmt.Println(document["document_uri"])

The response contains the document’s properties:

{
  "created": 1791125416372,
  "template_id": "bet2oQirk0pSd9ctH9Qu",
  "name": "Business Letter",
  "format": ".pdf",
  "data_length": 1,
  "filename": "Business Letter.pdf",
  "document_uri": "https://firebasestorage.googleapis.com/v0/b/storage.us.docugenerate.com/o/documents%2FiESelthRt4uaYQRTemrL%2FBusiness%20Letter.pdf?alt=media&token=c4b259ec-249b-4b35-a62f-6a8dc0da75f3",
  "id": "iESelthRt4uaYQRTemrL"
}

The output_format can be .docx (the default), .pdf, .doc, .odt, .txt, .html, .png or a PDF/A version. You can also merge PDF files at the end of the generated document with merge_with, or add attachments with attach.

Downloading the file
The document_uri points to the generated file, which you can download and save to disk:

file, err := http.Get(document["document_uri"].(string))
if err != nil {
	panic(err)
}
defer file.Body.Close()

if file.StatusCode >= 400 {
	panic(fmt.Sprintf("Download failed with status %d", file.StatusCode))
}

content, err := io.ReadAll(file.Body)
if err != nil {
	panic(err)
}
if err := os.WriteFile(document["filename"].(string), content, 0644); err != nil {
	panic(err)
}

Receiving the file directly
If you don’t want the document stored in the cloud, set the Accept header to application/octet-stream. The API then responds with the binary file instead of JSON:

payload, err := json.Marshal(map[string]any{
	"template_id": "bet2oQirk0pSd9ctH9Qu",
	"data": map[string]string{
		"Date":           "October 4, 2026",
		"Name":           "Emily Carter",
		"Job Title":      "Operations Manager",
		"Company Name":   "Harbor Point Consulting",
		"Street Address": "118 West Street",
		"City":           "Annapolis",
		"State":          "Maryland",
		"Zip Code":       "21405",
		"Email":          "emily.carter@example.com",
		"Phone":          "(410) 555-0142",
	},
	"output_format": ".pdf",
})
if err != nil {
	panic(err)
}

request, err := http.NewRequest("POST", apiURL+"/document", bytes.NewReader(payload))
if err != nil {
	panic(err)
}
request.Header.Set("Authorization", apiKey)
request.Header.Set("Accept", "application/octet-stream")
request.Header.Set("Content-Type", "application/json")

response, err := http.DefaultClient.Do(request)
if err != nil {
	panic(err)
}
defer response.Body.Close()

body, err := io.ReadAll(response.Body)
if err != nil {
	panic(err)
}

if response.StatusCode >= 400 {
	panic(fmt.Sprintf("DocuGenerate API error %d: %s", response.StatusCode, body))
}

if err := os.WriteFile("Business Letter.pdf", body, 0644); err != nil {
	panic(err)
}
fmt.Println(response.Header.Get("X-Document-Id"))

Batch document generation
To generate several documents in one request, pass a slice of maps as data. A document is generated for each map:

payload, err := json.Marshal(map[string]any{
	"template_id": "bet2oQirk0pSd9ctH9Qu",
	"data": []map[string]string{
		{"Date": "October 4, 2026", "Name": "Emily Carter", "Job Title": "Operations Manager", "Company Name": "Harbor Point Consulting", "Street Address": "118 West Street", "City": "Annapolis", "State": "Maryland", "Zip Code": "21405", "Email": "emily.carter@example.com", "Phone": "(410) 555-0142"},
		{"Date": "October 4, 2026", "Name": "Daniel Brooks", "Job Title": "Logistics Coordinator", "Company Name": "Northfield Logistics", "Street Address": "2400 South Lamar Boulevard", "City": "Austin", "State": "Texas", "Zip Code": "78704", "Email": "daniel.brooks@example.com", "Phone": "(512) 555-0187"},
	},
	"output_format": ".pdf",
	"single_file":   true,
	"page_break":    true,
})
if err != nil {
	panic(err)
}

request, err := http.NewRequest("POST", apiURL+"/document", bytes.NewReader(payload))
if err != nil {
	panic(err)
}
request.Header.Set("Authorization", apiKey)
request.Header.Set("Accept", "application/json")
request.Header.Set("Content-Type", "application/json")

response, err := http.DefaultClient.Do(request)
if err != nil {
	panic(err)
}
defer response.Body.Close()

body, err := io.ReadAll(response.Body)
if err != nil {
	panic(err)
}

if response.StatusCode >= 400 {
	panic(fmt.Sprintf("DocuGenerate API error %d: %s", response.StatusCode, body))
}

var document map[string]any
if err := json.Unmarshal(body, &document); err != nil {
	panic(err)
}

By default, all the documents are combined in a single file, with a page break after each one. Set page_break to false to remove the page breaks.

When single_file is false, a file is generated per data object and all the files are grouped in a .zip archive. Use the name parameter to name the archive, and output_name with merge tags to give each file a dynamic name, such as Letter for [Name]:

payload, err := json.Marshal(map[string]any{
	"template_id":   "bet2oQirk0pSd9ctH9Qu",
	"data":          []map[string]string{...},
	"output_format": ".pdf",
	"single_file":   false,
	"name":          "Business Letters",
	"output_name":   "Letter for [Name]",
})

This generates a Business Letters.zip archive containing Letter for Emily Carter.pdf and Letter for Daniel Brooks.pdf. The merge tags in output_name must use the same delimiters as the template.

Using a data file
To generate documents in bulk from an Excel or CSV file, send the file in a multipart/form-data request. A document is generated for each row of the spreadsheet. If the file has several sheets, specify the sheet parameter to choose which one to use.

file, err := os.Open("Data.xlsx")
if err != nil {
	panic(err)
}
defer file.Close()

var form bytes.Buffer
writer := multipart.NewWriter(&form)
writer.WriteField("template_id", "bet2oQirk0pSd9ctH9Qu")
part, err := writer.CreateFormFile("file", "Data.xlsx")
if err != nil {
	panic(err)
}
if _, err := io.Copy(part, file); err != nil {
	panic(err)
}
writer.WriteField("output_format", ".pdf")
writer.Close()

request, err := http.NewRequest("POST", apiURL+"/document", &form)
if err != nil {
	panic(err)
}
request.Header.Set("Authorization", apiKey)
request.Header.Set("Accept", "application/json")
request.Header.Set("Content-Type", writer.FormDataContentType())

response, err := http.DefaultClient.Do(request)
if err != nil {
	panic(err)
}
defer response.Body.Close()

body, err := io.ReadAll(response.Body)
if err != nil {
	panic(err)
}

if response.StatusCode >= 400 {
	panic(fmt.Sprintf("DocuGenerate API error %d: %s", response.StatusCode, body))
}

Using a data file is another form of batch generation, so the same parameters apply for combining generated documents in a single file or grouping them in a .zip archive with a custom name for each file.

8. List documents

A GET /document request returns the documents generated from a template, whose ID is passed in the template_id query parameter:

request, err := http.NewRequest("GET", apiURL+"/document?template_id=bet2oQirk0pSd9ctH9Qu", nil)
if err != nil {
	panic(err)
}
request.Header.Set("Authorization", apiKey)
request.Header.Set("Accept", "application/json")

response, err := http.DefaultClient.Do(request)
if err != nil {
	panic(err)
}
defer response.Body.Close()

body, err := io.ReadAll(response.Body)
if err != nil {
	panic(err)
}

if response.StatusCode >= 400 {
	panic(fmt.Sprintf("DocuGenerate API error %d: %s", response.StatusCode, body))
}

var documents []map[string]any
if err := json.Unmarshal(body, &documents); err != nil {
	panic(err)
}
for _, document := range documents {
	fmt.Println(document["id"], document["name"], document["document_uri"])
}

9. Get document

To retrieve a single document, call GET /document/{id} with its ID:

documentID := "iESelthRt4uaYQRTemrL"

request, err := http.NewRequest("GET", apiURL+"/document/"+documentID, nil)
if err != nil {
	panic(err)
}
request.Header.Set("Authorization", apiKey)
request.Header.Set("Accept", "application/json")

response, err := http.DefaultClient.Do(request)
if err != nil {
	panic(err)
}
defer response.Body.Close()

body, err := io.ReadAll(response.Body)
if err != nil {
	panic(err)
}

if response.StatusCode >= 400 {
	panic(fmt.Sprintf("DocuGenerate API error %d: %s", response.StatusCode, body))
}

var document map[string]any
if err := json.Unmarshal(body, &document); err != nil {
	panic(err)
}

10. Update document

A PUT /document/{id} request renames a document, which is the only property that can be updated:

documentID := "iESelthRt4uaYQRTemrL"

payload, err := json.Marshal(map[string]string{"name": "Letter for Emily Carter"})
if err != nil {
	panic(err)
}

request, err := http.NewRequest("PUT", apiURL+"/document/"+documentID, bytes.NewReader(payload))
if err != nil {
	panic(err)
}
request.Header.Set("Authorization", apiKey)
request.Header.Set("Accept", "application/json")
request.Header.Set("Content-Type", "application/json")

response, err := http.DefaultClient.Do(request)
if err != nil {
	panic(err)
}
defer response.Body.Close()

body, err := io.ReadAll(response.Body)
if err != nil {
	panic(err)
}

if response.StatusCode >= 400 {
	panic(fmt.Sprintf("DocuGenerate API error %d: %s", response.StatusCode, body))
}

var document map[string]any
if err := json.Unmarshal(body, &document); err != nil {
	panic(err)
}

11. Delete document

To delete a document, send a DELETE /document/{id} request. The API responds with a 204 No Content status on success:

documentID := "iESelthRt4uaYQRTemrL"

request, err := http.NewRequest("DELETE", apiURL+"/document/"+documentID, nil)
if err != nil {
	panic(err)
}
request.Header.Set("Authorization", apiKey)
request.Header.Set("Accept", "application/json")

response, err := http.DefaultClient.Do(request)
if err != nil {
	panic(err)
}
defer response.Body.Close()

body, err := io.ReadAll(response.Body)
if err != nil {
	panic(err)
}

if response.StatusCode >= 400 {
	panic(fmt.Sprintf("DocuGenerate API error %d: %s", response.StatusCode, body))
}